package com.hyacinth.lotterysystem.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hyacinth.lotterysystem.common.constant.Constants;
import com.hyacinth.lotterysystem.common.utils.JacksonUtil;
import com.hyacinth.lotterysystem.common.utils.RedisUtil;
import com.hyacinth.lotterysystem.dao.entity.*;
import com.hyacinth.lotterysystem.dao.mapper.*;
import com.hyacinth.lotterysystem.dto.param.Winner;
import com.hyacinth.lotterysystem.dto.req.DrawPrizeReq;
import com.hyacinth.lotterysystem.dto.req.WinningRecordReq;
import com.hyacinth.lotterysystem.dto.resp.WinningRecordResp;
import com.hyacinth.lotterysystem.service.DrawPrizeService;
import com.hyacinth.lotterysystem.service.WinningRecordService;
import com.hyacinth.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static com.hyacinth.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.hyacinth.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private static final Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);

    @Resource
    private ActivityMapper activityMapper;

    @Resource
    private ActivityPrizeMapper activityPrizeMapper;

    @Resource
    private ActivityUserMapper activityUserMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private PrizeMapper prizeMapper;

    @Resource
    private WinningRecordMapper winningRecordMapper;

    @Resource
    private WinningRecordService winningRecordService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeReq req) {
        // 发消息
        Map<String, String> map = new HashMap<>();
        map.put("message", String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(req));
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);
        logger.info("mq消息发送成功: map = {}", JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeReq req) {
        // 活动是否存在以及有效
        Long activityId = req.getActivityId();
        ActivityDO activityDO = activityMapper.selectById(activityId);
        if (activityDO == null || !activityDO.valid())
            return false;
        // 奖品是否有效
        Long prizeId = req.getPrizeId();
        LambdaQueryWrapper<ActivityPrizeDO> activityPrizeDOLambdaQueryWrapper = Wrappers.lambdaQuery(ActivityPrizeDO.class)
                .eq(ActivityPrizeDO::getPrizeId, prizeId)
                .eq(ActivityPrizeDO::getActivityId, activityId);
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectOne(activityPrizeDOLambdaQueryWrapper);
        if (activityPrizeDO == null || activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name()))
            return false;
        // 中奖者人数是否等于奖品数量
        List<Winner> winnerList = req.getWinnerList();
        if (winnerList.size() != activityPrizeDO.getPrizeAmount())
            return false;
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeReq req) {
        // 查询活动相关的信息 活动 人员 奖品
        Long activityId = req.getActivityId();
        ActivityDO activityDO = activityMapper.selectById(activityId);

        List<Long> userIds = req.getWinnerList().stream()
                .map(Winner::getUserId)
                .toList();
        List<UserDO> userDOList = userMapper.selectBatchIds(userIds);

        Long prizeId = req.getPrizeId();
        PrizeDO prizeDO = prizeMapper.selectById(prizeId);
        LambdaQueryWrapper<ActivityPrizeDO> activityPrizeDOLambdaQueryWrapper = Wrappers.lambdaQuery(ActivityPrizeDO.class)
                .eq(ActivityPrizeDO::getActivityId, activityId)
                .eq(ActivityPrizeDO::getPrizeId, prizeId);
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectOne(activityPrizeDOLambdaQueryWrapper);
        // 构造中奖者记录
        List<WinningRecordDO> winningRecordDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    winningRecordDO.setActivityId(activityId);
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeId);
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinningTime(req.getWinningTime());
                    return winningRecordDO;
                }).toList();
        winningRecordService.saveBatch(winningRecordDOList);
        // 缓存中奖记录
        // 奖品维度
        cacheWinningRecords(Constants.REDIS_KEY_DRAW_PRIZE_INFO + activityId + ":" + prizeId, winningRecordDOList, Constants.DRAW_RESULT_TIME_OUT);

        // 缓存活动维度中奖记录
        if (!activityDO.valid()) {
            LambdaQueryWrapper<WinningRecordDO> winningRecordDOLambdaQueryWrapper = Wrappers.lambdaQuery(WinningRecordDO.class)
                    .eq(WinningRecordDO::getActivityId, activityId);
            List<WinningRecordDO> winningRecordDOS = winningRecordMapper.selectList(winningRecordDOLambdaQueryWrapper);
            cacheWinningRecords(Constants.REDIS_KEY_DRAW_ACTIVITY_INFO + activityId, winningRecordDOS, Constants.DRAW_RESULT_TIME_OUT);
        }

        return winningRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        // 删除数据表
        logger.info("删除中奖记录 -> activityId:{}, prizeId:{}", activityId, prizeId);
        if (activityId == null && prizeId == null)
            return;
        LambdaQueryWrapper<WinningRecordDO> lambdaQueryWrapper = new LambdaQueryWrapper<>(WinningRecordDO.class)
                .eq(activityId != null, WinningRecordDO::getActivityId, activityId)
                .eq(prizeId != null, WinningRecordDO::getPrizeId, prizeId);
        winningRecordMapper.delete(lambdaQueryWrapper);
        // 删除缓存
        if (prizeId != null) {
            deleteCacheWinningRecords(Constants.REDIS_KEY_DRAW_PRIZE_INFO + activityId + ":" + prizeId);
        }
        deleteCacheWinningRecords(Constants.REDIS_KEY_DRAW_ACTIVITY_INFO + activityId);
    }

    @Override
    public List<WinningRecordResp> getWinningRecords(WinningRecordReq param) {
        // 1.先查询redis
        String key = null;
        if (param.getPrizeId() == null) {
            key = Constants.REDIS_KEY_DRAW_ACTIVITY_INFO + param.getActivityId();
        } else {
            key = Constants.REDIS_KEY_DRAW_PRIZE_INFO + param.getActivityId() + ":" + param.getPrizeId();
        }
        List<WinningRecordDO> winningRecordDOList = getWinningRecords(key);
        if (!CollectionUtils.isEmpty(winningRecordDOList)) {
            return winningRecordDOList.stream().map(winningRecordDO -> {
                WinningRecordResp winningRecordResp = new WinningRecordResp();
                BeanUtils.copyProperties(winningRecordDO, winningRecordResp);
                return winningRecordResp;
            }).toList();
        }
         
        // 2.如果redis中不存在,查询数据库
        LambdaQueryWrapper<WinningRecordDO> winningRecordDOLambdaQueryWrapper = Wrappers.lambdaQuery(WinningRecordDO.class)
                .eq(WinningRecordDO::getActivityId, param.getActivityId())
                .eq(param.getPrizeId() != null, WinningRecordDO::getPrizeId, param.getPrizeId());
        winningRecordDOList = winningRecordMapper.selectList(winningRecordDOLambdaQueryWrapper);

        // 3.重新缓存到redis中
        cacheWinningRecords(key, winningRecordDOList, Constants.DRAW_RESULT_TIME_OUT);
        return winningRecordDOList.stream().map(winningRecordDO -> {
            WinningRecordResp winningRecordResp = new WinningRecordResp();
            BeanUtils.copyProperties(winningRecordDO, winningRecordResp);
            return winningRecordResp;
        }).toList();
    }

    private void deleteCacheWinningRecords(String key) {
        try {
            redisUtil.del(key);
        } catch (Exception e) {
            logger.error("删除中奖记录缓存异常,key: {}", key);
        }
    }

    private void cacheWinningRecords(String key, List<WinningRecordDO> winningRecordDOList, long time) {
        if (!StringUtils.hasText(key) || CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.warn("要缓存的内容为空！key:{}, value:{} ", key, winningRecordDOList);
            return;
        }
        String value = JacksonUtil.writeValueAsString(winningRecordDOList);
        try {
            redisUtil.set(key, value, time);
        } catch (Exception e) {
            logger.error("缓存中奖记录异常！key:{}, value:{}", key, value);
        }
    }

    private List<WinningRecordDO> getWinningRecords(String key) {
        String str = redisUtil.get(key);
        if (!StringUtils.hasText(str) || !StringUtils.hasText(str))
            return List.of();

        List<WinningRecordDO> winningRecordDOList = JacksonUtil.readListValue(str, WinningRecordDO.class);
        return winningRecordDOList;
    }
}
